(ns read.readDB
    "This namespace contains functions that demonstrate how to read a binary 
    file. It specifically allows reading the URLyBird database file."  
    (:import 
	(java.io FileInputStream DataInputStream RandomAccessFile File)
	(java.nio ByteBuffer)
	(java.nio.CharBuffer) 
	(java.nio.channels FileChannel)
	(javax.swing JOptionPane JFrame) 

	)
	)

;-------------------------------------------------------------------------------
;;; Uncomment this function if using Clojure 1.1 Alpha
(defn byte-array 
  "Return an array of byte primitives of size n."
  [n]
  (make-array Byte/TYPE n))

;-------------------------------------------------------------------------------

(defn read-str-len
  "Read the next n bytes from file and create a string."
  [file n]
  (let [barray (byte-array n)]
    (.readFully file barray)
    (String. barray)))
         
;-------------------------------------------------------------------------------
(defn read-fields-meta
  "Reads the meta-data of all the fields from the given file. You must specify 
  the total number of fields."
  [file num-fields]
  (loop [n num-fields
         resultado []]
    (if (= n 0)
        resultado
        (let [name-len   (.readShort file)
              field-name (read-str-len file name-len)
              field-len  (.readShort file)]
             (recur (dec n) (conj resultado [field-name field-len]))))))                                     

;-------------------------------------------------------------------------------
(defn read-one-record
  "Reads one individual record from the given file. You must specify a vector 
  with the fields' meta-data."
  [file fields]
  (loop 
    [result {:deleted (not (zero? (.readShort file)))}
     [[fname fsize] & rest-fields] fields]
     (if fname
         (recur (assoc result (keyword fname) (.trim (read-str-len file fsize)))
                 rest-fields)
         result)))          

;-------------------------------------------------------------------------------
(defn read-records
  "Reads all the individual records of the file."
  [file fields]
  (loop 
    [result []]
    (if (zero? (.available file))
     
		result
        (recur (conj result (read-one-record file fields))))))
    
;-------------------------------------------------------------------------------
(defn read-bin-file
  "Reads a binary file and returns a vector with its information."
  [file-name]
  (with-open [file (DataInputStream. (FileInputStream. file-name))]
    (let [magic-number (.readInt file)
          offset       (.readInt file)
          num-fields   (.readShort file)
          fields-meta  (read-fields-meta file num-fields)
          records      (read-records file fields-meta)
		  ]
      {:magic magic-number :offset offset :num-fields num-fields 
       :fields fields-meta :records records})))
       
;      
  
;-------------------------------------------------------------------------------
(defn write-empty-row
  "Writes an empty row at the end of the total records."
  [name numOfRecords]
  (let[
		fileToRead (File. "db-1x2.db")
		fileRA	(RandomAccessFile. fileToRead "rw")
		fileChannel (.getChannel fileRA)
		buffer (ByteBuffer/allocate 161)
		bufferCopy (ByteBuffer/allocate 161)
		magic-number (.readInt fileRA)
        offset       (.readInt fileRA)
        num-fields   (.readShort fileRA)
        fields-meta  (read-fields-meta fileRA num-fields)
	  ]
	
	(.position fileChannel (+ offset (* 161 numOfRecords)))
	(println (str "FileChannel before allocation length: " (.size fileChannel)))
	
	(.write fileChannel buffer)
	;(println (str "Number of bytes read: " (.read fileChannel bufferCopy)))
	(println (str "FileChannel after insertion length: " (.size fileChannel)))
	
	(.close fileChannel)
	(.close fileRA)
  )
)

(defn fill 
"Fills the buffer with new values. 
 Receives two parameters: the value in bufer, the name where value will be located" 
  [buffer string]
  (.put buffer (.getBytes string "US-ASCII"))) 

(defn write-row-with-values
"Write in the database the new values. 
 Receives nine parameters: database file, number of row, value name, location value, size value, smoking value, rate value, date value, owner value" 
  [fileName numOfRow name location size smoking rate date owner]
 
  (let
  	[frame     	(JFrame.)
  	 nameBuffer 		(ByteBuffer/allocate 64)
		 locationBuffer (ByteBuffer/allocate 64)
		 sizeBuffer 		(ByteBuffer/allocate 4)
		 smokingBuffer 	(ByteBuffer/allocate 1)
		 rateBuffer 		(ByteBuffer/allocate 8)
		 dateBuffer 		(ByteBuffer/allocate 10)
		 ownerBuffer 	  (ByteBuffer/allocate 8)]
		(try 
		  (fill nameBuffer name)  
		  (fill locationBuffer location)
		  (fill sizeBuffer size)
		  (fill smokingBuffer smoking)
		  (fill rateBuffer rate)
		  (fill dateBuffer date)
		  (fill ownerBuffer owner)
    (catch Exception _ (JOptionPane/showMessageDialog frame "The value you enter in the field is incorrect. Please re-enter the value."))
    )
		(with-open [fileRA	(RandomAccessFile. fileName "rw")]
		  (.readInt fileRA)
      (let [offset (.readInt fileRA)]
        (.seek fileRA (+ offset (* 161 numOfRow)))
        (.writeShort fileRA 0)
        (.write fileRA (.array nameBuffer))
        (.write fileRA (.array locationBuffer))
        (.write fileRA (.array sizeBuffer))
        (.write fileRA (.array smokingBuffer))
        (.write fileRA (.array rateBuffer))
        (.write fileRA (.array dateBuffer))
        (.write fileRA (.array ownerBuffer))))
     )
     
     
 ) 

(defn erase-record 
 "Erases the record given. Receives two parameters: the data base file, position of the record to delete"
 [fileName numOfRow]
 
 	(with-open [fileRA	(RandomAccessFile. fileName "rw")]
		  (.readInt fileRA); read magic number
      (let [offset (.readInt fileRA)]
        (.seek fileRA (+ offset (* 161 numOfRow)))
        (.writeShort fileRA 0x8000)))
)

 